/// 工厂模块
module factories.factory;

import factories.common;
import factories.generated;
import factories.storage;
import factories.map;
import factories.procs;
import factories.util;

/// 流水线列表长度
uint procLen;
/// 物品变化列表长度
uint itemDeltaLen;

/// 区块
struct Chunk {
	/// 资源
	ushort[Resource.max + 1] resource;
}

/// 节点
union Node {
	/// 生产线
	Process* process;

	/// 仓库
	Storage* storage;

	private void* ptr;

	/// 是否为仓库
	@property bool isStorage() const =>
		ptr >= &storages && ptr < cast(void*)&storages + storages.sizeof;
}

/// 获取仓库节点位置
@property uint bound(Node[] nodes) {
	uint i = 0;
	foreach (node; nodes) {
		if (!node.isStorage)
			break;
		i++;
	}
	return i;
}

@persist() Chunk[map.length >> 4] chunks;

/// 添加物品变化
extern (C) export bool addItemDelta(Items id, int count) {
	if (itemDeltaLen >= procs.data.length)
		return false;
	procs.data[itemDeltaLen++] = ItemDelta(id, count);
	return true;
}

/// 开始/结束添加流水线并返回流水线ID
extern (C) export uint nextProc() {
	procs.indices[procLen] = itemDeltaLen;
	return procLen++;
}

/// 网络
struct Network {
	/// 节点列表
	Node[] nodes;

	/// 排序
	void sort() {
		const len = nodes.length;
		if (len < 2)
			return;
		for (uint l, r; r < len; r++) {
			if (nodes[r].isStorage) {
				while (nodes[l].isStorage) {
					l++;
					if (l > r)
						r = l;
					if (r >= len)
						return;
				}
				if (!nodes[l].isStorage) {
					auto t = nodes[l];
					nodes[l] = nodes[r];
					nodes[r] = t;
					l++;
				}
				if (l > r)
					r = l;
			}
		}
	}
}

alias IdMap = Map!(int, uint, maxStorages);

/// 网络列表
struct Networks {
	/// 所属网络
	int[map.length] id;

	/// 第i个网络的仓库组和工序ID列表
	MultiArray!(Node, maxStorages, map.length) net;

	alias net this;

	/// 网络ID映射
	IdMap netIds;

	/// 添加节点
	private void add(uint slot) {
		enum int[4] dx = [1, -1, mapWidth, -mapWidth];

		Queue!(uint, map.length / 16) q;
		q.push(slot);
		id[slot] = slot;
		while (!q.empty) {
			uint netId = q.front;
			q.pop();
			const f1 = transFlags(netId);
			if (!f1)
				continue;
			foreach (d; dx) {
				uint i = netId + d;
				if (i < map.length && map[i].level && id[i] == -1) {
					const f2 = transFlags(i);
					if ((f1 & f2) || (f1 == 0 && f2 != 0) || (f1 != 0 && f2 == 0))
						q.push(i);
					id[i] = id[slot];
				}
			}
		}
	}

	// 预处理：离散化netId，计算第i个网络节点数量
	bool preprocess() {
		auto lastNet = net;
		net.indices[] = 0;
		netIds.clear();
		foreach (uint i; 0 .. map.length) {
			const slot = id[i];
			if (slot >= 0) {
				uint netId = void;
				if (auto p = slot in netIds)
					netId = *p;
				else {
					netId = netIds.length;
					if (!netIds.set(slot, netId)) {
						net = lastNet;
						return false;
					}
				}
				if (i in storages || canWork(i))
					net.indices[netId + 1]++;
			}
		}
		return true;
	}

	/// 构建网络
	bool build() {
		import core.stdc.string;

		memset(id.ptr, 255, id.sizeof);
		foreach (uint i; 0 .. map.length) {
			if (map[i].level) {
				if (id[i] == -1) {
					if (canWork(i)) {
						add(i);
					}
				}
			}
		}
		/// 离散化网络ID
		if (!preprocess())
			return false;
		auto indices = net.indices[];
		indices.toPrefixSum();
		/// 第i个网络的仓库组和工序ID列表
		uint[maxStorages] netSize;
		foreach (uint i; 0 .. map.length) {
			const slot = id[i];
			if (slot >= 0) {
				uint netId = netIds[slot];
				Node node;
				if (auto s = i in storages) {
					node = Node(storage : s);
				} else {
					auto p = &proc[i];
					if (p.recipe >= 0)
						node = Node(p);
					else
						continue;
				}
				const index = indices[netId] + netSize[netId]++;
				net.data[index] = node;
			}
		}
		// 排序
		foreach (i; 0 .. netIds.length) {
			Network(net[i]).sort();
		}
		return true;
	}

	void update(uint tick) {
		foreach (i; 0 .. netIds.length) {
			.update(net[i], tick);
		}
	}
}

Networks net;

/// 获取可传输的物品类型
@property uint transFlags(Buildings b) {
	import factories.storage;

	switch (b) with (Buildings) {
	case road, bridge, railway, railwayBridge, railwayTunnel:
		return 1 << StorageType.solid | 1 << StorageType.radioactive;
	case fluidPipe:
		return 1 << StorageType.liquid | 1 << StorageType.gas;
	case warehouse, vacuumPipe:
		return 1 << StorageType.solid | 1 << StorageType.liquid | 1 << StorageType.radioactive;
	default:
	}
	return storageFlags(b);
}

/// 获取可传输的物品类型
@property uint transFlags(uint slot) {
	const building = map[slot].building;
	uint flags = building.transFlags;
	if (!flags) {
		const recipe = proc[slot].recipe;
		if (recipe >= 0) {
			foreach (d; procs[recipe]) {
				flags |= 1 << d.id.storageType;
			}
		}
	}
	return flags;
}

/// 是否可以工作
bool canWork(uint slot) {
	auto p = proc[slot];
	return p.recipe >= 0;
}

/// 图块改变时
bool onChange(const uint slot, uint value) {
	const level = value >> 8 & 15;
	if (!level) { // 拆除建筑物
		storages.remove(slot);
		return true;
	}
	if (level && value >> 12 == 0) {
		const flags = storageFlags(cast(Buildings)value);
		if (flags) {
			auto s = slot in storages;
			if (!s) {
				if (!storages.set(slot, Storage()))
					return false;
				s = slot in storages;
			}
			s.updateCapacities(flags, level);
		}
	}
	return net.build();
}

/// 更新所有网络
extern (C) export void updateNet(uint tick) {
	net.update(tick);
}

/// 更新网络
void update(Node[] nodes, uint tick) {
	const pos = nodes.bound;
	auto stores = cast(Storage*[])nodes[0 .. pos];
	foreach (p; cast(Process*[])nodes[pos .. nodes.length]) {
		int k = p.k;
		int t = 1;
		while (t <= k)
			t <<= 1;
		t >>= 1;
		int done = 0;
		while (k && t) {
			if (k >= t && tryProduce(p, stores, tick, t).status == 0) {
				k -= t;
				done += t;
			} else {
				t >>= 1;
			}
		}
		// 更新进度
		const slot = p - proc.ptr;
		if (slot >= 0 && slot < map.length)
			map[slot].highlight = p.k ? 15 * done / p.k : 0;
		if (!done && p.period)
			p.step = cast(ubyte)(p.step - 1 + p.period) % p.period;
	}
}
